package pruefungsPlanung;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.JOptionPane;

import einlesenUndDatenverwaltung.*;
import ausgabeUndDatenverwaltung.Anwesenheit;
import ausgabeUndDatenverwaltung.AnwesenheitsListe;
import ausgabeUndDatenverwaltung.AusgabeListen;
import ausgabeUndDatenverwaltung.NotenListe;
import ausgabeUndDatenverwaltung.NotenlistenEintrag;
import ausgabeUndDatenverwaltung.OutputErzeuger;
import ausgabeUndDatenverwaltung.PlatzKarte;
import ausgabeUndDatenverwaltung.PlatzKartenListe;
import ausgabeUndDatenverwaltung.PruefungsTag;

/**
 * Die Klasse PruefungsPlanung ist die zentrale Rechenlogikklasse. In dieser
 * Klasse wird unter Beruecksichtigung bestimmter Vorbedingungen (insb. pro
 * Student maximal drei Pruefungen pro Tag) ein Pruefungsplan ermittelt, der die
 * Anzahl der Pruefungstage minimiert.
 * 
 * @author (Hendrik Hertel, Max Bogner)
 * @version (12.12.2013)
 */
public class PruefungsPlanung {
	private KursListe kursliste;
	private StudentenListe studentenliste;
	private Student student;
	private Kurs kurs;
	private AusgabeListen al;

	/**
	 * Konstruktor zur Erzeugung der Objekte der Klasse PruefungsPlanung
	 */
	public PruefungsPlanung() {

	}

	/**
	 * Ermitteln des optimalen Pruefungsplans, der die Anzahl der Pruefungstage
	 * minimiert
	 * 
	 * author: Hendrik Hertel, Max Bogner
	 * 
	 * @param BuchungsListe
	 *            ,
	 * @param KursListe
	 *            ,
	 * @param StudentenListe
	 */

	public void optimiere(BuchungsListe bl, KursListe kl, StudentenListe sl) {
		// erstellt die Kompositionsklasse fuer alle Ausgabelisten
		al = new AusgabeListen();
		//bl = bl.makeUnique(bl, sl);
		bl.makeKorrektBuchungsliste(sl.getStudentenliste(), kl.getKursliste());
		// Kurse die keine Buchungen enthalten werden herausgefiltert
		kl = filternGebuchterKurse(kl, bl);
		// bl.fixBuchungsliste();
		// Methode, die die notenliste erstellt
		NotenListe nl = new NotenListe();
		nl.createNotenlisteFromLists(bl, kl, sl);
		al.setNotenliste(nl);

		// Erstellen von Platzkartenlisten
		// 1. Erstellen der orginaeren Platzkartenliste
		PlatzKartenListe pkl = new PlatzKartenListe();

		// 2. Erstellen einer Platzkartenliste, die am Ende der Pruefungsplanung
		// die Platzkarten fuer alle Pruefungstage enthaelt
		PlatzKartenListe pklGesamt = new PlatzKartenListe();

		// 3. Erstellen einer Platzkartenliste, die im Rahmen der
		// Pruefungsplanung die Platzkarten mit den verschobenen Kursen enthaelt
		PlatzKartenListe pklRemoved = new PlatzKartenListe();

		// 4. Erstellen einer Platzkartenliste, die im Rahmen der
		// Pruefungsplanung die Platzkarten enthaelt, deren Kursliste mehr als
		// drei Kurse umfasst
		PlatzKartenListe pklMehrAlsDreiKurse = new PlatzKartenListe();

		// Starten des Planungsalgothythmus
		System.out.println();
		System.out.println();
		System.out
				.println("--------------------------------------------------");
		System.out.println("Start der Pruefungsplanung");
		System.out
				.println("--------------------------------------------------");
		System.out.println();
		System.out.println();

		// Initialisieren der orginären Platzkartenliste mit einer Platzkarte
		// je Student, die eine Liste aller von dem Studenten gebuchten Kurse
		// enthaelt
		pkl = getAnzahlBuchungenStudent(bl, sl, kl);

		// Deklarieren und Initialisieren einer Variable vom Typ Integer zum
		// Zaehlen der Iterationen des Planungsalgorhythmus
		int zaehler = 0;

		// Pruefen ob ein Student aktuell mehr als x (hier: 3) Kurse pro Tag hat
		while (pkl.einStudentHatMehrKurseProTagAls(3)) {

			// Inkrementieren des Iterationszaehlers
			zaehler = zaehler + 1;

			// Erstellen einer neuen Kursliste
			KursListe kl2 = new KursListe();

			// tbd
			Boolean weiter = true;

			// Ausgeben des aktuellen Werts des Iterationszaehlers
			System.out.println("Iteration: " + zaehler);

			// Erstellen eines neuen Vectors
			Vector<Kurs> vecKlone = new Vector<Kurs>();

			// tbd
			vecKlone = (Vector<Kurs>) kl.getKursliste().clone();

			// tbd
			kl2.setKursliste(vecKlone);

			// tbd
			// Erstellen einer Platzkartenliste
			PlatzKartenListe pklM = new PlatzKartenListe();
			do {

				// Initialisieren der originaeren Platzkartenliste mit einer
				// absteigend in Bezug auf die Anzahl der Kurse in der
				// jeweiligen Kursliste sortierten Variante der Platzkartenliste
				pkl.getPlatzKartenListeSortedByAnzahlKurs();

				// Initialisieren der entsprechenden Platzkartenliste mit einer
				// Platzkartenliste, die alle Platzkarten enthaelt, deren
				// Kursliste mehr als x (hier: 3) Kurse umfasst
				pklMehrAlsDreiKurse = pkl.getPlatzKartenMitMehrKursenAls(3);

				// Erstellen eines Kurses
				Kurs KursMitAmMeistenBuchungen = new Kurs();

				// Ermitteln des in den Kurslisten der Platzkarten der neuen
				// Platzkartenliste am häufigsten vorhanden (gebuchten) Kurses
				KursMitAmMeistenBuchungen = pklMehrAlsDreiKurse
						.getKursMitAmMeistenStudenten(pklMehrAlsDreiKurse, kl2);

				// Entfernen des Kurses aus den Kurslisten der Platzkarten in
				// der Platzkartenliste

				pklM = pkl.removePlatzKarteByKurs(KursMitAmMeistenBuchungen);

				pklRemoved.getPlatzkartenliste().addAll(
						pklM.getPlatzkartenliste());

				// Pruefen ob noch immer eine Kursliste einer Platzkartenliste
				// mehr als 3 Kurse umfasst
				weiter = pkl.einStudentHatMehrKurseProTagAls(3);

				// Entfernen des Kurses aus der Kursliste
				kl2.removeKursByKursId(KursMitAmMeistenBuchungen);

			} while (weiter);

			// pklRemoved.makePlatzKartenUniqueByStudent();
			pklRemoved.makePlatzKartenUniqueByStudentNEW();

			// Erstellen eines Pruefungstages
			PruefungsTag pt = new PruefungsTag();
			// PruefungsTag ptc = new PruefungsTag();
			// Setzen des Namens des Pruefungstages in Abhaengigkeit der
			// aktuellen Iteration des Planungsalgorhythmus
			pt.setName("pt" + zaehler);
			// ptc.setName("pt" + zaehler);
			//
			pt.addPruefungstagToPruefungstageListe(pt);
			// ptc.addPruefungstagToPruefungstageListe(ptc);
			// Hinzufuegen des Pruefungstages zu den Platzkarten in der
			// originaeren Paltzkartenliste
			pkl.setPruefungstagForAllPlatzkarten(pt);

			// tbd
			pklGesamt.getPlatzkartenliste().addAll(pkl.getPlatzkartenliste());
			pkl.getPlatzkartenliste().removeAllElements();
			// pkl = pklRemoved;
			pkl.setPlatzkartenliste((Vector<PlatzKarte>) pklRemoved
					.getPlatzkartenliste().clone());
			pklRemoved.getPlatzkartenliste().removeAllElements();
			// .;

			kl2.setKursliste(kl.getKursliste());

		}

		// Erstellen eines Pruefungstages
		PruefungsTag pt = new PruefungsTag();
		// PruefungsTag ptc = new PruefungsTag();
		// Setzen des Namens des Pruefungstages in Abhaengigkeit der
		// aktuellen Iteration des Planungsalgorhythmus
		zaehler = zaehler + 1;
		pt.setName("pt" + zaehler);
		// ptc.setName("pt" + zaehler);
		// ptc.addPruefungstagToPruefungstageListe(ptc);
		pt.addPruefungstagToPruefungstageListe(pt);

		// Hinzufuegen des Pruefungstages zu den Platzkarten in der
		// originaeren Paltzkartenliste
		pkl.setPruefungstagForAllPlatzkarten(pt);

		// hier muss fuer die Platzkartenliste des letzten Pruefungstag noch der
		// Tag gesetzt werden
		// Ausserdem muss der letzte Pruefungstag noch der gesamten
		// Platzkartenliste hinzugefuegt werden
		pklGesamt.getPlatzkartenliste().addAll(pkl.getPlatzkartenliste());

		al.setPlatzkartenliste(pklGesamt);

		Vector<AnwesenheitsListe> awl = new Vector<AnwesenheitsListe>();
		// Erstellt die Anwesenheitslisten aus der Platzkartenliste
		// Gibt eine Liste mit Anwesenheitslisten zurueck, erstellt eine
		// Anwesenheitsliste pro Uni und pro Tag
		awl = al.getAnwesenheitsliste()
				.createAnwesenheitslistenAusPlatzkartenListe(pklGesamt);
		//
		al.setAnwesenheitslistenprotagunduni(awl);
		// Erstellt die Pruefungsterminplaene mithilfe der Platzkartenliste
		al.getPruefungsplan().createPruefungsterminPlanFromPlatzkartenListe(
				pklGesamt);

		// erstellt den Outputerzeuger
		OutputErzeuger oe = new OutputErzeuger();
		// speichert die alle vorhandenen Dateien der Ausgabeliste als Dateien
		// im Dateisystem
		oe.speichereAufDateiSystem(al);

	}

	// wird verwendet
	// Buchungsliste muss sortiert sein nach Studentenid (aufsteigend)
	// Erstellt die Platzkarten.Kursliste fuer jeden Student.
	/**
	 * 
	 * Buchungsliste muss sortiert sein nach Studentenid (aufsteigend) Erstellt
	 * die Platzkarten.Kursliste fuer jeden Student.
	 * 
	 * @author Max Bogner / Hendrik Hertel
	 * @param bl
	 * @param sl
	 * @param kl
	 * @return
	 */
	public PlatzKartenListe getAnzahlBuchungenStudent(BuchungsListe bl,
			StudentenListe sl, KursListe kl) {
		// Abfrage, ob vector leer ist (also 0 Objekte enthaelt)
		int zaehler = 0;
		int aktuelleStudentid = -99;
		int alteStudentid = 0;
		int kursid = 0;
		int alteKursid = -99;
		// ArrayList anzBuchungenStudent = new ArrayList();
		PlatzKartenListe pkl = new PlatzKartenListe();
		if (bl.getBuchungsliste().size() > 0) {

			// Schleife, die alle Objekte der Buchungsliste durchlaeuft
			for (Enumeration e = bl.getBuchungsliste().elements(); e
					.hasMoreElements();) {
				Buchung buchung2 = (Buchung) e.nextElement();
				KursListe kurslisteProStudent;

				PlatzKarte pk;

				// Studentenid
				aktuelleStudentid = buchung2.getStudent();
				// Kursid-> damit Kurse holen
				kursid = buchung2.getKurs();

				// Studentenid nicht gleich mit vorhergehender
				if (aktuelleStudentid != alteStudentid) {
					kurslisteProStudent = new KursListe();
					pk = new PlatzKarte();
					kurslisteProStudent.addElement(kl.getKursImproved(kursid));

					pk.setStudent(sl.getStudent(aktuelleStudentid));
					pk.setKursliste(kurslisteProStudent);
					pkl.addElement(pk);

				}
				// Studentenid gleich (hochz�hlen und Anh�ngen)-> ab dem
				// zweiten
				// gleichen Eintrag
				else if (aktuelleStudentid == alteStudentid) {
					// erster Kurs = alter Kurs

					pkl.getPlatzkartenliste().lastElement().getKursliste()
							.addElement(kl.getKursImproved(kursid));

				}

				// Umsetzen der Werte fuer den naechsten Schleifendurchlauf
				alteKursid = kursid;
				alteStudentid = aktuelleStudentid;

			}

		} else {
			// Erzeugen einer Fehlerausgabe in einem Fenster und im
			// Terminalfenster
			JOptionPane.showMessageDialog(null,
					"Die angegebene Liste enthaelt keine Objekte.",
					"listAllTerm Hinweis", JOptionPane.INFORMATION_MESSAGE);
		}


		return pkl;
	}

	/**
	 * Methode zum Unique machen von Kurslisten. Gibt Kursliste nur mit
	 * gebuchten Kursen aus. Alle anderen Kurse werden nicht gespeichert.
	 * 
	 * 
	 * @author: Kerstin und Daniel
	 * 
	 * @param kl
	 *            (KursListe) die Kursliste
	 * @param bl
	 *            (BuchungsListe) die Buchungsliste
	 * @return gebkl (KursListe) Kursliste, die nur gebuchte Kurse enthaelt
	 */
	public KursListe filternGebuchterKurse(KursListe kl, BuchungsListe bl) {
		// gebuchte Kursliste
		KursListe gebkl = new KursListe();

		// Abfrage, ob vector leer ist (also 0 Objekte enthaelt)
		if (bl.anzBuchungen() > 0) {
			int aktuelleKursid = 0;
			int vergleichsOjekt = 0;
			int altes_aktObj = 0;

			// Ablaufen der Kursliste
			for (int i = 0; i < kl.anzKurse(); i++) {

				// Vergleichsobjekt aus Kursliste holen-> Kursid
				vergleichsOjekt = kl.getKursAtId(i).getKursid();

				// Studentenliste, in der die Studenten=Buchungen gespeichert
				// werden
				Vector<Integer> studentenliste = new Vector<Integer>();

				// Schleife, die alle Objekte der Buchungsliste durchlaeuft
				for (int j = 0; j < bl.anzBuchungen(); j++) {

					Buchung buchung = (Buchung) bl.getBuchungAtIndex(j);
					// Kursid
					aktuelleKursid = buchung.getKurs();

					// Kursids von Buchungsliste und Vergleichobjekt aus
					// Kursliste gleich-> zaeher hochzaehlen
					if (aktuelleKursid == vergleichsOjekt) {
						// fuer die Abfrage ob Buchungen vorliegen
						int studentid = buchung.getStudent();
						studentenliste.add(studentid);

						// umsetzen Variable, fuer richtigen Kurs
						altes_aktObj = aktuelleKursid;
					}
				}
				// nur hinzufuegen wenn eine oder mehr als eine Buchung vorliegt
				if (studentenliste.size() > 0) {
					// holen des Kurses mit der aktuellen Kursid
					Kurs kurs = kl.getKursImproved(altes_aktObj);

					// Hinzufuegen des Kurses mit Buchungen zum Kursvector
					gebkl.addElement(kurs);
					;
				}
			}

			System.out.println("Groesse:" + gebkl.anzKurse());

		} else {
			// Erzeugen einer Fehlerausgabe
			System.out.println("Liste enthaelt keine Objekte");
		}
		// kursliste mit gebuchten kursen
		return gebkl;
	}

	/**
	 * Gibt die Variable vom Typ AusgabeListen zurueck.
	 * 
	 * @Author: Kerstin Schulze-> brauchen wir fuer die Ausgaben!
	 * @return al (AugabeListen) Das Objekt vom Typ AusgabeListen
	 */
	public AusgabeListen getAl() {
		return al;
	}

	/**
	 * Methode zum setzen der Variable AusgabeListen
	 * 
	 * @Author: Kerstin Schulze-> brauchen wir fuer die Ausgaben!
	 * @param al
	 *            (AusgabeListen) Die zu setzende AusgabeListe
	 */
	public void setAl(AusgabeListen al) {
		this.al = al;
	}

}
